| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- import { NextRequest, NextResponse } from "next/server";
- import { STORAGE_KEY, internalAllowedWebDavEndpoints } from "../../../constant";
- import { getServerSideConfig } from "@/app/config/server";
- const config = getServerSideConfig();
- const mergedAllowedWebDavEndpoints = [
- ...internalAllowedWebDavEndpoints,
- ...config.allowedWebDavEndpoints,
- ].filter((domain) => Boolean(domain.trim()));
- const normalizeUrl = (url: string) => {
- try {
- return new URL(url);
- } catch (err) {
- return null;
- }
- };
- async function handle(
- req: NextRequest,
- { params }: { params: { path: string[] } },
- ) {
- if (req.method === "OPTIONS") {
- return NextResponse.json({ body: "OK" }, { status: 200 });
- }
- const folder = STORAGE_KEY;
- const fileName = `${folder}/backup.json`;
- const requestUrl = new URL(req.url);
- let endpoint = requestUrl.searchParams.get("endpoint");
- let proxy_method = requestUrl.searchParams.get("proxy_method") || req.method;
- // Validate the endpoint to prevent potential SSRF attacks
- if (
- !endpoint ||
- !mergedAllowedWebDavEndpoints.some((allowedEndpoint) => {
- const normalizedAllowedEndpoint = normalizeUrl(allowedEndpoint);
- const normalizedEndpoint = normalizeUrl(endpoint as string);
- return (
- normalizedEndpoint &&
- normalizedEndpoint.hostname === normalizedAllowedEndpoint?.hostname &&
- normalizedEndpoint.pathname.startsWith(
- normalizedAllowedEndpoint.pathname,
- )
- );
- })
- ) {
- return NextResponse.json(
- {
- error: true,
- msg: "Invalid endpoint",
- },
- {
- status: 400,
- },
- );
- }
- if (!endpoint?.endsWith("/")) {
- endpoint += "/";
- }
- const endpointPath = params.path.join("/");
- const targetPath = `${endpoint}${endpointPath}`;
- // only allow MKCOL, GET, PUT
- if (
- proxy_method !== "MKCOL" &&
- proxy_method !== "GET" &&
- proxy_method !== "PUT"
- ) {
- return NextResponse.json(
- {
- error: true,
- msg: "you are not allowed to request " + targetPath,
- },
- {
- status: 403,
- },
- );
- }
- // for MKCOL request, only allow request ${folder}
- if (proxy_method === "MKCOL" && !targetPath.endsWith(folder)) {
- return NextResponse.json(
- {
- error: true,
- msg: "you are not allowed to request " + targetPath,
- },
- {
- status: 403,
- },
- );
- }
- // for GET request, only allow request ending with fileName
- if (proxy_method === "GET" && !targetPath.endsWith(fileName)) {
- return NextResponse.json(
- {
- error: true,
- msg: "you are not allowed to request " + targetPath,
- },
- {
- status: 403,
- },
- );
- }
- // for PUT request, only allow request ending with fileName
- if (proxy_method === "PUT" && !targetPath.endsWith(fileName)) {
- return NextResponse.json(
- {
- error: true,
- msg: "you are not allowed to request " + targetPath,
- },
- {
- status: 403,
- },
- );
- }
- const targetUrl = targetPath;
- const method = proxy_method || req.method;
- const shouldNotHaveBody = ["get", "head"].includes(
- method?.toLowerCase() ?? "",
- );
- const fetchOptions: RequestInit = {
- headers: {
- authorization: req.headers.get("authorization") ?? "",
- },
- body: shouldNotHaveBody ? null : req.body,
- redirect: "manual",
- method,
- // @ts-ignore
- duplex: "half",
- };
- let fetchResult;
- try {
- fetchResult = await fetch(targetUrl, fetchOptions);
- } finally {
- console.log(
- "[Any Proxy]",
- targetUrl,
- {
- method: method,
- },
- {
- status: fetchResult?.status,
- statusText: fetchResult?.statusText,
- },
- );
- }
- return fetchResult;
- }
- export const PUT = handle;
- export const GET = handle;
- export const OPTIONS = handle;
- export const runtime = "edge";
|